/*
The MIT License (MIT)
Copyright (c) 2013 Dalvik Boss dalvikboss@gmail.com

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package sidl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.OutputStream;
import java.util.LinkedList;

import org.antlr.runtime.ANTLRFileStream;
import org.antlr.runtime.CommonTokenStream;

import sidl.generator.GeneratorContext;
import sidl.generator.c.AnsicGenerator;
import sidl.generator.c.AnsicHeaderGenerator;
import sidl.generator.csharp.CSharpGenerator;
import sidl.generator.java.JavaFileGenerator;
import sidl.generator.qt.QtFileGenerator;
import sidl.generator.scala.ScalaGenerator;
import sidl.generator.xcode.XCodeFileGenerator;

public class Main {

	static String input = null;
	static LinkedList<String> outDirs = new LinkedList<String>();
	static LinkedList<String> langs = new LinkedList<String>();

	/**
	 * @param args
	 * @throws Throwable
	 */
	public static void main(String[] args) throws Throwable {

		for (int i = 0; i < args.length; i++) {
			if (args[i].equals("-lang")) {
				if (i == args.length - 1) {
					help();
				} else {
					langs.add(args[i + 1]);
					i++;
				}
			} else if (args[i].equals("-s")) {
				if (i == args.length - 1) {
					help();
				} else {
					input = args[i + 1];
					i++;
				}
			} else if (args[i].equals("-d")) {
				if (i == args.length - 1) {
					help();
				} else {
					outDirs.add(args[i + 1]);
					i++;
				}
			} else {
				System.err.println("bad arg " + args[i]);
				help();
			}
		}

		if (langs.size() == 0) {
			System.err.println("no lang");
			help();
		}
		if (outDirs.size() == 0) {
			System.err.println("no outdir(s) ");
			help();
		}

		File in = new File(input);
		if (in.isDirectory()) {
			File[] sidlFiles = in.listFiles(new FilenameFilter() {

				@Override
				public boolean accept(File arg0, String arg1) {
					return arg1.endsWith(".sidl");
				}
			});
			if (sidlFiles == null || sidlFiles.length == 0) {
				System.err.println("no .sidl file in " + input);
				help();
			} else {
				for (File sidl : sidlFiles) {
					process(sidl);
				}
			}
		} else {
			process(in);
		}

	}

	private static void process(File in) throws Throwable {

		System.out.println("processing " + in.getAbsolutePath());
		ANTLRFileStream fs = new ANTLRFileStream(in.getAbsolutePath());
		SidlLexer lex = new SidlLexer(fs);
		CommonTokenStream cts = new CommonTokenStream(lex);
		SidlParser par = new SidlParser(cts);
		par.compilationUnit();
		par.doc.clazzes.addAll(Clazz.getReservedClazzes());
		addReservedMethods(par.doc.mainInf());

		for (String outDir : outDirs) {
			for (String lang : langs) {
				if (lang.equals("c")) {
					generate_c(par.doc, in.getAbsolutePath(), outDir);
				} else if (lang.equals("java")) {
					generate_java(par.doc, in.getAbsolutePath(), outDir);
				} else if (lang.equals("qt")) {
					generate_qt(par.doc, in.getAbsolutePath(), outDir);
				} else if (lang.equals("xcode")) {
					generate_xcode(par.doc, in.getAbsolutePath(), outDir);
				} else if (lang.equals("scala")) {
					generate_scala(par.doc, in.getAbsolutePath(), outDir);
				} else if (lang.equals("csharp")) {
					generate_csharp(par.doc, in.getAbsolutePath(), outDir);

				} else if (lang.equals("all")) {
					generate_c(par.doc, in.getAbsolutePath(), outDir);
					generate_java(par.doc, in.getAbsolutePath(), outDir);
					generate_qt(par.doc, in.getAbsolutePath(), outDir);
					generate_xcode(par.doc, in.getAbsolutePath(), outDir);
					generate_scala(par.doc, in.getAbsolutePath(), outDir);
					generate_csharp(par.doc, in.getAbsolutePath(), outDir);

				} else {
					System.err.println("unknown lang " + lang);
					help();
				}
			}

		}

	}

	private static void addReservedMethods(Inf mainInf) {
		// Func f ;
		// f = new Func();
		// f.name = "openStream";
		// f.params.add(new Param(new Type("String"), "path"));
		// f.rtype = new Type("int");
		//
		// mainInf.funcs.add(f);

	}

	private static void generate_c(Doc doc, String inFile, String outDir)
			throws Throwable {
		// Clazz clz = new Clazz();
		// clz.name = "cstring";
		// clz.fields.add(new Field(new Type("int"), "size"));
		// clz.fields.add(new Field(new Type("char[]"), "elems"));
		// doc.clazzes.add(clz);
		System.out.printf("generating c source to %s\n", outDir);
		File dir = new File(outDir);
		dir.mkdirs();
		String outFile = new File(dir, doc.packageName.replace('.', '_') + "_"
				+ doc.mainInf().name + ".c").getAbsolutePath();
		OutputStream out = new FileOutputStream(outFile);
		GeneratorContext context = new GeneratorContext();
		context.out = out;
		context.doc = doc;
		AnsicGenerator g = new AnsicGenerator(context);
		g.generate();
		out.close();
		generate_h(doc, inFile, outDir);
	}

	private static void generate_h(Doc doc, String inFile, String outDir)
			throws Throwable {
		System.out.printf("generating c header to %s\n", outDir);
		File dir = new File(outDir);
		dir.mkdirs();
		String outFile = new File(dir, doc.packageName.replace('.', '_') + "_"
				+ doc.mainInf().name + ".h").getAbsolutePath();
		OutputStream out = new FileOutputStream(outFile);
		GeneratorContext context = new GeneratorContext();
		context.out = out;
		context.doc = doc;
		AnsicHeaderGenerator g = new AnsicHeaderGenerator(context);
		g.generate();
		out.close();
	}

	private static void generate_java(Doc doc, String inFile, String outDir)
			throws Throwable {
		System.out.printf("generating java code to %s/%s\n", outDir,
				doc.packageName.replace('.', '/'));
		File dir = new File(outDir + File.separator
				+ doc.packageName.replace('.', File.separatorChar));
		dir.mkdirs();
		String outFile = new File(dir, doc.mainInf().name + ".java")
				.getAbsolutePath();
		OutputStream out = new FileOutputStream(outFile);
		GeneratorContext context = new GeneratorContext();
		context.out = out;
		context.doc = doc;
		JavaFileGenerator g = new JavaFileGenerator(context);
		g.generate();
		out.close();
	}

	private static void generate_qt(Doc doc, String inFile, String outDir)
			throws Throwable {
		System.out.printf("generating qt code to %s\n", outDir);
		File dir = new File(outDir);
		dir.mkdirs();
		QtFileGenerator qf = new QtFileGenerator(doc, outDir);
	}

	private static void generate_xcode(Doc doc, String inFile, String outDir)
			throws Throwable {
		System.out.printf("generating xcode code to %s\n", outDir);
		File dir = new File(outDir);
		dir.mkdirs();
		XCodeFileGenerator xf = new XCodeFileGenerator(doc, outDir);
	}

	private static void generate_scala(Doc doc, String inFile, String outDir)
			throws Throwable {

		System.out.printf("generating scala code to %s/%s\n", outDir,
				doc.packageName.replace('.', '/'));
		File dir = new File(outDir + File.separator
				+ doc.packageName.replace('.', File.separatorChar));
		dir.mkdirs();
		String outFile = new File(dir, doc.mainInf().name + ".scala")
				.getAbsolutePath();

		OutputStream out = new FileOutputStream(outFile);
		GeneratorContext context = new GeneratorContext();
		context.out = out;
		context.doc = doc;
		ScalaGenerator g = new ScalaGenerator(context);
		g.generate();
		out.close();
	}

	private static void generate_csharp(Doc doc, String inFile, String outDir)
			throws Throwable {
		System.out.printf("generating c# code to %s\n", outDir);
		File dir = new File(outDir);
		dir.mkdirs();
		String outFile = new File(dir, doc.mainInf().name + ".cs")
				.getAbsolutePath();
		OutputStream out = new FileOutputStream(outFile);
		GeneratorContext context = new GeneratorContext();
		context.out = out;
		context.doc = doc;
		CSharpGenerator g = new CSharpGenerator(context);
		g.generate();
		out.close();
	}

	private static void help() {
		System.err.println("usage java -jar sidl.jar <options>");
		System.err.println("options:");
		System.err
				.printf("    -lang <lang> -where lang is one of c, java, qt, xcode, scala, csharp\n");
		System.err
				.println("    -s <input file|input dir> -where input file is the source .sidl file or directory includes *.sidl files");
		System.err
				.println("    -d <out dir> -where out dir is the output directory");

		System.exit(-1);

	}

}
